
// ===============================================================================================================
// -*- C++ -*-
//
// Sprite.hpp - Declaration of the Sprite class.
//
// Copyright (c) 2011 Guilherme R. Lampert
// guilherme.ronaldo.lampert@gmail.com
//
// This code is licenced under the MIT license.
//
// This software is provided "as is" without express or implied
// warranties. You may freely copy and compile this source into
// applications you distribute provided that the copyright text
// above is included in the resulting source code.
//
// ===============================================================================================================

#ifndef __SPRITE_HPP__
#define __SPRITE_HPP__

#include <Common.hpp>
#include <Image.hpp>

///
/// Sprite -- A sprite manager class.
/// Controls an internal list of images that can be displayed as a sequence composing an animation.
///
/// The Sprite class is reference counted and cloneable.
///
class Sprite : public ReferenceCountable {

public:

	// Static Methods:

	/// Creates a new instance of the Sprite class.
	/// \param [in] imageList: Array of images to compose this sprite. (Optional)
	/// \param [in] count: Number of images in the imageList array. Ignored if imageList is null.
	/// \param [in] cloneImages: If true, the images are cloned, else just a reference is used. Ignored if imageList is null.
	/// \param [in] perFrameDelay: Per frame delay in seconds. Ignored if imageList is null.
	static Sprite * Create(Image * const imageList[], int count, bool cloneImages, float perFrameDelay);

	// Public Interface:

	/// Add a new image (frame) to the sprite animation sequence.
	/// \return Zero based index of the image in the internal sprite list, or -1 on failure.
	bool AddImage(const Image * img, bool cloneImage, float frameDelay);

	/// Release all the sprite images.
	void Clear(void);

	/// Set the frame delay (in seconds) for each image in the sprite.
	void SetPerFrameDelay(float frameDelay);

	/// Get the current sprite frame for drawing based on the elapsed time.
	Image * GetFrame(float elapsedTime) const;

	/// Get the current frame.
	Image * GetCurrentFrame(void) const;

	/// Get a sequence of sprite frame for drawing.
	/// This method returns one image for each times it is called, starting whith the image
	/// pointed by firstFrame and incrementing the internal pointer until it reaches and returns the image pointed by lastFrame.
	/// The image reference count is incremented to ensure the image remains valid
	/// until no longer needed, so call Release() on the return when done.
	Image * GetFrameSequence(float elapsedTime, int firstFrame, int lastFrame) const;

	/// Number of images (frames) this sprite has.
	int GetNumImages(void) const;

	/// Retruns a clone of the sprite.
	Sprite * Clone(void) const;

	// ReferenceCountable Methods:

	virtual unsigned long AddRef(void) const;

	virtual unsigned long Release(void) const;

	virtual unsigned long ReferenceCount(void) const;

protected:

	virtual ~Sprite(void);

private:

	// This will prevent the class from being staticaly allocated.
	// The only way to create an instance of it now is thru the Sprite::Create() method.
	Sprite(void);

	// Helper constructor:
	Sprite(Image * const imageList[], int count, bool cloneImages, float perFrameDelay);

	// Disable copy.
	Sprite(const Sprite &);
	Sprite & operator = (const Sprite &);

	///
	/// Frame information.
	/// Holds the frame image pointer and the frame delay.
	///
	struct Frame {

		Image * image; ///< The frame image
		float delay;   ///< Frame delay in seconds (how much time this frame is displayed)

		struct Frame * next; ///< Next frame in the animation chain
	};

private:

	// Sprite frames in a linked list:

	Frame * frameList; ///< First frame of the animation chain
	mutable Frame * currentFrame; ///< Current frame of the animation chain

	int numFrames; ///< Number of image frames in the sprite linked list
	mutable float timeCounter; ///< Internal time counter
};

///
/// Reference countable sprite pointer type.
///
typedef RefPtr<Sprite> SpritePtr;

#endif // __SPRITE_HPP__